package com.sinodata.bsm.center.service.event;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.sinodata.bsm.center.bean.Condition;
import com.sinodata.bsm.center.bean.EventBean;
import com.sinodata.bsm.center.bean.Page;
import com.sinodata.bsm.center.bean.PropValueBean;
import com.sinodata.bsm.center.bean.ReduceEventDynamicBean;
import com.sinodata.bsm.center.bean.RelationHelper;
import com.sinodata.bsm.center.bean.ResBean;
import com.sinodata.bsm.center.bean.TreeData;
import com.sinodata.bsm.center.cache.EventTypeCache;
import com.sinodata.bsm.center.cache.NewEventCache;
import com.sinodata.bsm.center.cache.PropertyCache;
import com.sinodata.bsm.center.cache.PropertyValueCache;
import com.sinodata.bsm.center.cache.ResCache;
import com.sinodata.bsm.center.cache.ResTypeCache;
import com.sinodata.bsm.center.dao.impl.EventDaoImpl;
import com.sinodata.bsm.center.dao.impl.NotifyDaoImpl;
import com.sinodata.bsm.center.engine.event.EventEngine;
import com.sinodata.bsm.center.engine.resource.health.ResHealthEngine;
import com.sinodata.bsm.center.expression.Expressions;
import com.sinodata.bsm.center.web.action.integration.IntegEventDispatch;
import com.sinodata.bsm.common.constants.DataTypeConstants;
import com.sinodata.bsm.common.constants.EventConstants;
import com.sinodata.bsm.common.vo.Event;
import com.sinodata.bsm.common.vo.EventCauses;
import com.sinodata.bsm.common.vo.EventPropHis;
import com.sinodata.bsm.common.vo.EventRemark;
import com.sinodata.bsm.common.vo.EventType;
import com.sinodata.bsm.common.vo.Notify;
import com.sinodata.bsm.common.vo.Property;
import com.sinodata.bsm.common.vo.Res;
import com.sinodata.bsm.common.vo.ResType;

/**
 * <p>
 * Description: 事件管理服务类
 * </p>
 *
 * @author liulibo
 * @version 1.0

 * <p>
 * History: 
 *
 * Date                     Author         Version     Description
 * ---------------------------------------------------------------------------------
 * 2012-5-25 AM 9:53:50     liulibo        1.0         To create
 * </p>
 *
 * @since 
 * @see     
 */
@Service
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class EventService {

    @Autowired
    private NewEventCache newEventCache;

    @Autowired
    private ResTypeCache resTypeCache;

    @Autowired
    private PropertyValueCache propertyValueCache;

    @Autowired
    private EventDaoImpl eventDao;

    @Autowired
    private NotifyDaoImpl notifyDao;

    @Autowired
    private ResCache resCache;

    @Autowired
    private EventTypeCache eventTypeCache;

    @Autowired
    private PropertyCache propertyCache;

    private final static Logger logger = Logger.getLogger(EventService.class);

    /**
     * 新事件入库，不更新缓存，同时记录产生该事件的指标值
     * 
     * @param event
     * @throws Exception 
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void add(Event event) throws Exception {
        eventDao.save(event);
        //保存历史指标值
        //非故障类事件（健康度事件、恢复事件）， 无表达式不记录故障值
        if (EventConstants.CATALOG_FAULTEVENT != event.getCatalog() || event.getRuleExpr() == null) {
            return;
        }
        List<String> nodes = Expressions.parsePropNodes(event.getRuleExpr());
        List<EventPropHis> list = new ArrayList<EventPropHis>();
        for (String node : nodes) {
            String[] ids = node.split("[,]");
            Long propId = Long.valueOf(ids[1]);
            Property property = propertyCache.get(propId);
            if (property.getDataTypeId() == DataTypeConstants.DATA_TYPE_OBJECT) {
                continue;
            }
            PropValueBean propValueBean = propertyValueCache.getValueBean(event.getResId(), propId);
            EventPropHis eventPropHis = new EventPropHis();
            eventPropHis.setEventId(event.getId());
            eventPropHis.setResId(event.getResId());
            eventPropHis.setEventOccurTime(event.getOccurTime());
            eventPropHis.setPropCollectTime(propValueBean.getTime());
            eventPropHis.setPropId(propId);
            //TODO 数据类型处理
            eventPropHis.setPropValue(propValueBean.getValue());

            list.add(eventPropHis);
        }
        eventDao.batchSave(list);
    }

    /**
     * 更新事件，不更新缓存（用于更新事件产生次数）
     * 
     * @param event
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void update(Event event) {
        eventDao.update(event);
    }

    /**
     * 认领事件，从缓存获取数据，set值
     * 
     * @param eventId 事件Id
     * @param ackUser 认领人
     * @param remarks 认领备注
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void ack(Long eventId, String ackUser, String remarks) {
        Event event = newEventCache.get(eventId);
        if (event != null) {
            ack(event, ackUser, remarks);
        }
    }

    /**
     * 认领事件，私有方法
     * 
     * @param event 事件
     * @param ackUser 认领人
     * @param remarks 认领备注
     */
    @Transactional(propagation = Propagation.REQUIRED)
    private void ack(Event event, String ackUser, String remarks) {
        event.setAckTime(new Date());
        event.setAckUser(ackUser);
        event.setStatus(EventConstants.STATUS_ACKNOWLEDGED);
        eventDao.update(event);
        if (remarks != null && !"".equals(remarks)) {
            EventRemark eventRemark = new EventRemark();
            eventRemark.setEventId(event.getId());
            eventRemark.setRemarks(remarks);
            eventRemark.setTime(new Date());
            eventRemark.setUser(ackUser);
            eventDao.save(eventRemark);
        }
        List<Notify> notifyList = notifyDao.findByEvent(event.getId());
        resume(notifyList);
    }

    /**
     * 批量认领事件，从缓存获取数据，set值
     * 
     * @param eventIds
     * @param ackUser
     * @param remarks
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void batchAck(Long[] eventIds, String ackUser, String remarks) {
        List<Event> list = new ArrayList<Event>();
        List<EventRemark> remarkList = new ArrayList<EventRemark>();
        for (int i = 0; i < eventIds.length; i++) {
            Event event = newEventCache.get(eventIds[i]);
            event.setAckTime(new Date());
            event.setAckUser(ackUser);
            event.setStatus(EventConstants.STATUS_ACKNOWLEDGED);
            list.add(event);
            if (remarks != null && !"".equals(remarks)) {
                EventRemark eventRemark = new EventRemark();
                eventRemark.setEventId(event.getId());
                eventRemark.setRemarks(remarks);
                eventRemark.setTime(new Date());
                eventRemark.setUser(ackUser);
                remarkList.add(eventRemark);
            }
        }
        eventDao.batchSaveOrUpdate(list);
        eventDao.batchSaveOrUpdate(remarkList);
        //        for (Event event : list) {
        //            newEventCache.put(event.getId(), event);
        //        }
        List<Notify> notifyList = notifyDao.findByEvent(eventIds);
        resume(notifyList);
    }

    /**
     * 清除事件
     * 
     * @param eventId
     * @param confirmType
     * @param confirmer
     * @param remarks
     * @param isEngine
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void confirm(Long eventId, Integer confirmType, String confirmer, String remarker, String remarks, Boolean isEngine) {
        Event event = newEventCache.get(eventId);
        if (event != null) {
            confirm(event, confirmType, confirmer, remarker, remarks);
        }
        if (isEngine) {
            //事件引擎刷新状态
            EventEngine.refreshStatus(event.getResId(), event.getEventTypeId());
            //资源健康度引擎刷新资源健康度状态
            ResHealthEngine.getInstance().refreshResFromChild(ResBean.get(event.getResId()).res());
        }
        //add by dongyushi
        List<Notify> notifyList = notifyDao.findByEvent(eventId);
        resume(notifyList);
    }

    /**
     * 由外部接口清除BSM中的事件 
     * @param event
     * @param confirmType
     * @param confirmer
     * @param remarker
     * @param remarks
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void outSideConfirm(Event ievent, Integer confirmType, String confirmer, String remarker, String remarks, boolean isEngine) {
        Event event = newEventCache.getEventByObjectId(ievent.getObjectId(), ievent.getEventTypeId());
        if (event == null) {
            return;
        }
        logger.info("[TIP]\t外部清除一条事件  " + ievent.getObjectId());
        event.setStatus(EventConstants.STATUS_CONFIRMED);
        event.setConfirmType(confirmType);
        event.setConfirmer(confirmer);
        event.setConfirmTime(new Date());
        if (event.getAckUser() == null && EventConstants.CONFIRM_TYPE_USER == confirmType) {
            event.setAckUser(confirmer);
            event.setAckTime(event.getConfirmTime());
        }

        eventDao.update(event);
        if (remarks != null && !"".equals(remarks)) {
            EventRemark eventRemark = new EventRemark();
            eventRemark.setEventId(event.getId());
            eventRemark.setRemarks(remarks);
            eventRemark.setTime(new Date());
            if (remarker != null && !"".equals(remarker)) {
                eventRemark.setUser(remarker);
            } else {
                eventRemark.setUser(confirmer);
            }
            eventDao.save(eventRemark);
        }
        newEventCache.remove(event.getId());

        if (isEngine) {
            //事件引擎刷新状态
            EventEngine.refreshStatus(event.getResId(), event.getEventTypeId());
            //资源健康度引擎刷新资源健康度状态
            ResHealthEngine.getInstance().refreshResFromChild(ResBean.get(event.getResId()).res());
        }
        //add by dongyushi
        List<Notify> notifyList = notifyDao.findByEvent(event.getId());
        resume(notifyList);
    }

    /**
     * 清除事件
     * 
     * @param event 事件
     * @param confirmType 事件确认类型
     * @param confirmer 确认人
     * @param remarks  事件备注
     * @param isEngine 是否进入事件引擎
     */
    @Transactional(propagation = Propagation.REQUIRED)
    private void confirm(Event event, Integer confirmType, String confirmer, String remarker, String remarks) {
        event.setStatus(EventConstants.STATUS_CONFIRMED);
        event.setConfirmType(confirmType);
        event.setConfirmer(confirmer);
        event.setConfirmTime(new Date());
        if (event.getAckUser() == null && EventConstants.CONFIRM_TYPE_USER == confirmType) {
            event.setAckUser(confirmer);
            event.setAckTime(event.getConfirmTime());
        }

        //如果这是个集成事件且是用户确认的   则要在其它系统中把这个事件清除掉 
        if (event.getObjectId() != null && event.getObjectId().length() > 1 && EventConstants.CONFIRM_TYPE_USER == confirmType) {
            IntegEventDispatch.dispatchEvent(event);
        }

        eventDao.update(event);
        if (remarks != null && !"".equals(remarks)) {
            EventRemark eventRemark = new EventRemark();
            eventRemark.setEventId(event.getId());
            eventRemark.setRemarks(remarks);
            eventRemark.setTime(new Date());
            if (remarker != null && !"".equals(remarker)) {
                eventRemark.setUser(remarker);
            } else {
                eventRemark.setUser(confirmer);
            }
            eventDao.save(eventRemark);
        }
        newEventCache.remove(event.getId());
    }

    /**
     * 批量清除事件
     * 
     * @param eventIds
     * @param confirmType
     * @param confirmer
     * @param remarks
     * @param isEngine
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void batchConfirm(Long[] eventIds, Integer confirmType, String confirmer, String remarker, String remarks, Boolean isEngine) {
        List<Event> list = new ArrayList<Event>();
        List<EventRemark> remarkList = new ArrayList<EventRemark>();
        for (int i = 0; i < eventIds.length; i++) {
            Event event = newEventCache.get(eventIds[i]);
            event.setStatus(EventConstants.STATUS_CONFIRMED);
            event.setConfirmType(confirmType);
            event.setConfirmer(confirmer);
            event.setConfirmTime(new Date());
            if (event.getAckUser() == null && EventConstants.CONFIRM_TYPE_USER == confirmType) {
                event.setAckUser(confirmer);
                event.setAckTime(event.getConfirmTime());
            }

            //如果这是个集成事件且是用户确认的   则要在其它系统中把这个事件清除掉 
            if (event.getObjectId() != null && event.getObjectId().length() > 1 && EventConstants.CONFIRM_TYPE_USER == confirmType) {
                IntegEventDispatch.dispatchEvent(event);
            }

            list.add(event);
            if (remarks != null && !"".equals(remarks)) {
                EventRemark eventRemark = new EventRemark();
                eventRemark.setEventId(event.getId());
                eventRemark.setRemarks(remarks);
                eventRemark.setTime(new Date());
                if (remarker != null && !"".equals(remarker)) {
                    eventRemark.setUser(remarker);
                } else {
                    eventRemark.setUser(confirmer);
                }
                remarkList.add(eventRemark);
            }
        }
        eventDao.batchSaveOrUpdate(list);
        eventDao.batchSaveOrUpdate(remarkList);
        for (Event event : list) {
            newEventCache.remove(event.getId());
        }
        if (isEngine) {
            for (Event event : list) {
                //事件引擎刷新状态
                EventEngine.refreshStatus(event.getResId(), event.getEventTypeId());
                //资源健康度引擎刷新资源健康度状态
                ResHealthEngine.getInstance().refreshResFromChild(ResBean.get(event.getResId()).res());
            }
        }
        List<Notify> notifyList = notifyDao.findByEvent(eventIds);
        resume(notifyList);
    }

    /**
     * 取消通知
     * 
     * @param list
     */
    private void resume(List<Notify> list) {
        Long[] ids = new Long[list.size()];
        int i = 0;
        for (Notify notify : list) {
            ids[i] = notify.getId();
            i++;
        }
        notifyDao.batchDelete(ids);
    }

    public Page<EventBean> findEventByPage(Page<EventBean> page, Condition condition, String dimension) {
        String resTypeId = "";
        List<String> dimensionList = new ArrayList<String>();
        if (!dimension.equals("")) {
            String[] array = dimension.split("[|]");
            for (String scope : array) {
                if (scope.split(",")[0].equals("0-资源类别")) {
                    resTypeId = scope.split(",")[1];
                } else {
                    dimensionList.add(scope.split(",")[1]);
                }
            }
        }
        return eventDao.findEventByPage(page, condition, resTypeId, dimensionList);
    }

    public Event getEventById(Long id) {
        return (Event) eventDao.findById(new Event().getClass(), id);
    }

    public List<EventBean> findEventByCondition(Condition condition, String dimension) {
        String resTypeId = "";
        List<String> dimensionList = new ArrayList<String>();
        if (!dimension.equals("")) {
            String[] array = dimension.split("[|]");
            for (String scope : array) {
                if (scope.split(",")[0].split("-")[0].equals("0")) {
                    resTypeId = scope.split(",")[1];
                } else {
                    dimensionList.add(scope.split(",")[1]);
                }
            }
        }
        return eventDao.findEventByCondition(condition, resTypeId, dimensionList);

    }

    public EventBean findInformationById(Long id) {
        Condition condition = new Condition();
        Map<String, String> map = new HashMap<String, String>();
        map.put("id", String.valueOf(id));
        condition.setWhere(map);
        return eventDao.findEventByCondition(condition, "", new ArrayList<String>()).get(0);
    }

    public List<TreeData> getEventTypeTreeData(String eventTypeId) {
        String[] array = eventTypeId.split(",");
        Arrays.sort(array);
        List<TreeData> listResult = new ArrayList<TreeData>();
        List<ResType> listAll = resTypeCache.getAll();
        List<ResType> list = new ArrayList<ResType>();
        for (ResType resType : listAll) {
            if (resType.getParentId() == null) {
                TreeData treeData = new TreeData();
                treeData.setId(String.valueOf(resType.getId()));
                treeData.setName(resType.getName());
                treeData.setState("open");
                if (Arrays.binarySearch(array, String.valueOf(resType.getId())) > -1) {
                    treeData.setChecked(true);
                }
                listResult.add(treeData);
            } else {
                list.add(resType);
            }
        }
        //循环遍历其下子节点
        getResTypeTreeDataCycle(list, listResult, array);
        return listResult;
    }

    private void getResTypeTreeDataCycle(List<ResType> listScope, List<TreeData> listResult, String[] array) {
        for (TreeData treeData : listResult) {
            List<TreeData> children = new ArrayList<TreeData>();
            List<ResType> list = new ArrayList<ResType>();
            for (ResType resType : listScope) {
                if (String.valueOf(resType.getParentId()).equals(treeData.getId())) {
                    TreeData treeData1 = new TreeData();
                    treeData1.setId(String.valueOf(resType.getId()));
                    treeData1.setName(resType.getName());
                    if (Arrays.binarySearch(array, String.valueOf(resType.getId())) > -1) {
                        treeData1.setChecked(true);
                    }
                    children.add(treeData1);
                } else {
                    list.add(resType);
                }
            }
            if (children.size() != 0) {
                getResTypeTreeDataCycle(list, children, array);
                treeData.setChildren(children);
            }
        }
    }

    public List<EventRemark> findEventRemarkByEventId(Long eventId) {
        return eventDao.findEventRemarkByEventId(eventId);
    }

    public Integer checkEventAck(Long[] ids) {
        return eventDao.checkEventAck(ids);
    }

    public Integer checkEventConfirm(Long[] ids) {
        return eventDao.checkEventConfirm(ids);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addEventCauses(EventCauses eventCauses) {
        if (eventCauses.getId() == null) {
            eventDao.save(eventCauses);
        } else {
            eventDao.update(eventCauses);
        }
    }

    /**
     * 查询恢复事件，清除的事件
     * 
     * @param eventId
     * @return
     */
    public Event findEventByConfirmEventId(Long eventId) {
        return eventDao.findEventByConfirmEventId(eventId);
    }

    public EventCauses getEventCausesByEventId(Long eventId) {
        EventCauses eventCauses = eventDao.getEventCausesByEventId(eventId);
        return eventCauses;
    }

    public JSONObject resActiveEventPage(Page<Object> page, Condition condition, Map<String, Object> extensionParameter) {
        Page<Object> p = eventDao.resActiveEventPage(page, condition, extensionParameter);
        List<Object> list = p.getRows();

        JSONObject json = new JSONObject();
        JSONArray array = new JSONArray();
        int tempIndex = 0;
        for (int i = 0; i < list.size(); i++) {
            Object[] object = (Object[]) list.get(i);
            Object resId = object[tempIndex++];
            Res res = resCache.get(Long.parseLong(resId.toString()));
            Object eventTypeId = object[tempIndex++];
            EventType eventType = eventTypeCache.get(Long.parseLong(eventTypeId.toString()));
            Object level = object[tempIndex++];
            Object msg = object[tempIndex++];
            Object occurTime = object[tempIndex++];
            Object id = object[tempIndex++];
            Object statu = object[tempIndex++];

            JSONObject entity = new JSONObject();
            entity.element("checkbox", "<input type='checkbox' name='eventCheck' value='" + String.valueOf(id) + "'/>");
            entity.element("resName", res == null ? "" : res.getName());
            entity.element("resId", resId);
            entity.element("eventTypeName", eventType == null ? "" : eventType.getName());
            entity.element("eventTypeId", eventTypeId);
            entity.element("levelName", EventConstants.getLevelDesc(Integer.parseInt(level.toString())));
            entity.element("level", level);
            entity.element("statu", statu);
            entity.element("statuValue", EventConstants.getStatusDesc(Integer.parseInt(statu.toString())));
            entity.element("occurTime", occurTime == null ? "" : occurTime);
            entity.element("msg", msg == null ? "" : msg.toString());
            entity.element("id", id);
            array.add(entity);
            tempIndex = 0;
        }

        json.element("aaData", array);
        json.element("iTotalRecords", p.getTotal());
        json.element("iTotalDisplayRecords", p.getTotal());
        return json;
    }

    /**
     * 递归获取一个资源及其子资源的事件总数
     * 
     * @param resId
     * @return
     */
    public int getNewEventsCount(Long resId) {
        Set<ResBean> set = RelationHelper.childrenRec(resId);
        Long[] resIds = new Long[set.size()];
        int i = 0;
        Iterator<ResBean> it = set.iterator();
        while (it.hasNext()) {
            resIds[i] = it.next().res().getId();
            i++;
        }
        return newEventCache.getNewEventsCount(resIds);
    }

    /**
     * 获取单个资源及其子资源（递归）的活动事件
     * 
     * @param resId
     * @return
     */
    public List<Event> getNewEvents(Long resId) {
        List<Event> list = new ArrayList<Event>();
        Set<ResBean> set = RelationHelper.childrenRec(resId);
        for (ResBean resBean : set) {
            if (resBean == null || resBean.res() == null) {
                //logger.info("拓扑图中的资源ID或者该资源ID的子资源不存在。资源ID=" + resId);
                continue;
            }
            List<Event> temp = newEventCache.getNewEventsByResId(resBean.res().getId());
            if (temp != null && temp.size() > 0) {
                list.addAll(temp);
            }
        }
        return list;
    }

    public List<Event> getNewEvents(Date occurTime, Integer level) {
        return eventDao.getNewEvents(occurTime, level);
    }

    /**
     * 获取一组资源及其子资源（递归）的活动事件，已排重
     * 
     * @param resIds
     * @return
     */
    public List<Event> getNewEvents(Long[] resIds) {
        List<Event> list = new ArrayList<Event>();
        for (int i = 0; i < resIds.length; i++) {
            List<Event> temp = getNewEvents(resIds[i]);
            for (Event event : temp) {
                if (!list.contains(event)) {
                    list.add(event);
                }
            }
        }
        return list;
    }

    public List<ReduceEventDynamicBean> getDynamicEvent(String time, String reduceType) {
        return eventDao.getDynamicEvent(time, reduceType);
    }

    public List<Object[]> getActiveEventNum(String resId) {
        return eventDao.getActiveEventNum(resId);
    }
}